#ifndef __GLESHardwareBufferManager_H__
#define __GLESHardwareBufferManager_H__

#include "U2GLESPreRequest.h"
#include "U2HardwareBufferManager.h"


U2EG_NAMESPACE_BEGIN


// Default threshold at which glMapBuffer becomes more efficient than glBufferSubData (32k?)
#   define U2_GL_MAP_BUFFER_THRESHOLD (1024 * 32)

/** Implementation of U2HardwareBufferManager for OpenGL ES. */
class _U2GLESShare GLESHardwareBufferManagerBase : public U2HardwareBufferManagerBase
{
protected:
    char* mScratchBufferPool;
    U2_MUTEX(mScratchMutex)
    size_t mMapBufferThreshold;

public:
    GLESHardwareBufferManagerBase();
    virtual ~GLESHardwareBufferManagerBase();
    /// Creates a vertex buffer
    U2HardwareVertexBufferSharedPtr createVertexBuffer(size_t vertexSize,
        size_t numVerts, U2HardwareBuffer::Usage usage, bool useShadowBuffer = false);
    /// Create a hardware vertex buffer
    U2HardwareIndexBufferSharedPtr createIndexBuffer(
        U2HardwareIndexBuffer::IndexType itype, size_t numIndexes,
        U2HardwareBuffer::Usage usage, bool useShadowBuffer = false);

    /// Utility function to get the correct GL usage based on HBU's
    static GLenum getGLUsage(unsigned int usage);

    /// Utility function to get the correct GL type based on VET's
    static GLenum getGLType(unsigned int type);

    /** Allocator method to allow us to use a pool of memory as a scratch
        area for hardware buffers. This is because glMapBuffer is incredibly
        inefficient, seemingly no matter what options we give it. So for the
        period of lock/unlock, we will instead allocate a section of a local
        memory pool, and use glBufferSubDataARB / glGetBufferSubDataARB
        instead.
    */
    void* allocateScratch(u2uint32 size);

    /// @see allocateScratch
    void deallocateScratch(void* ptr);

	/** Threshold after which glMapBuffer is used and not glBufferSubData
    */
    const size_t getGLMapBufferThreshold() const;
    void setGLMapBufferThreshold( const size_t value );
};



/// GLESHardwareBufferManagerBase as a Singleton
class _U2GLESShare U2GLESHardwareBufferManager : public U2HardwareBufferManager
{
public:
	U2GLESHardwareBufferManager()
		: U2HardwareBufferManager(U2_NEW GLESHardwareBufferManagerBase()) 
	{

	}
	~U2GLESHardwareBufferManager()
	{
		U2_DELETE mImpl;
	}



	/// Utility function to get the correct GL usage based on HBU's
	static GLenum getGLUsage(unsigned int usage) 
	{ return GLESHardwareBufferManagerBase::getGLUsage(usage); }

	/// Utility function to get the correct GL type based on VET's
	static GLenum getGLType(unsigned int type)
	{ return GLESHardwareBufferManagerBase::getGLType(type); }

	/** Allocator method to allow us to use a pool of memory as a scratch
	area for hardware buffers. This is because glMapBuffer is incredibly
	inefficient, seemingly no matter what options we give it. So for the
	period of lock/unlock, we will instead allocate a section of a local
	memory pool, and use glBufferSubDataARB / glGetBufferSubDataARB
	instead.
	*/
	void* allocateScratch(u2uint32 size)
	{
		return static_cast<GLESHardwareBufferManagerBase*>(mImpl)->allocateScratch(size);
	}

	/// @see allocateScratch
	void deallocateScratch(void* ptr)
	{
		static_cast<GLESHardwareBufferManagerBase*>(mImpl)->deallocateScratch(ptr);
	}
    /** Threshold after which glMapBuffer is used and not glBufferSubData
	*/
	const size_t getGLMapBufferThreshold() const
	{
		return static_cast<GLESHardwareBufferManagerBase*>(mImpl)->getGLMapBufferThreshold();
	}
	void setGLMapBufferThreshold( const size_t value )
	{
		static_cast<GLESHardwareBufferManagerBase*>(mImpl)->setGLMapBufferThreshold(value);
	}

};


U2EG_NAMESPACE_END

#endif
